# !/usr/bin/env python
# -*- coding:utf-8 -*-

# __author__ = 'Yangqihui'

# import usocket as socket
# import ujson as json
# import uos as os

import socket,json,os

class Request(object):
    def __init__(self):
        self.body = ""
        self.url = "/"
        self.method = "GET"
        self.query = {}
        self.forms = {}
        self.file = b""
    
    def set(self,request_bytes):
        req = str(request_bytes,"utf-8")
        self.body = req
        req_list = req.split('\r\n')
        
        method, url, _ = req_list[0].split(" ")

        if url == "/favicon.ico":
            return

        else:
            self.method = method
            url = url.split("?")

            if len(url) == 2:
                self.url = url[0]
                self.query = dict([s.split("=") for s in url[1].split("&")])
            else:
                self.url = url[0]
                self.query = {}

            if req_list[-1]:
                self.forms = dict([s.split("=") for s in req_list[-1].split("&")])
            else:
                self.forms = {}

class Route:
    def __init__(self):
        self.default = "index.html"
        self.ext = ['html',"htm","css","js","ttf","png","gif","jpg","jpeg"]
        self.map = {"GET":{},"POST":{}}

    def route(self,path,method="GET"):
        ''' 有参修饰器 '''
        def wrapper(func):
            self.map[method][path] = func
            return func
        return wrapper

    # def isfile(self,path):
    #     try:
    #         return True if os.stat(filename)[6]>0 else False
    #     except:
    #         return False

    def jsonify(self,d):
        '''文档返回 JSON 格式'''
        if isinstance(d,dict):
            return json.dumps(d,separators=(',',':'))
        else:
            return str(d)

    def filebyte(self,filename):
        with open(filename,"rb") as f:
            return f.read()

    def load(self,method,path):
        try:
            # 文件
            filename,ext = path[1:].split(".")
            # 如果是文件*.*,不报错
            if ext in self.ext:
                try:
                    # print(f"{path[1:]}")
                    # 存在返回字节
                    return self.filebyte(path[1:])
                except:
                    # 不存在返回 None
                    return None
            else:
                return None
        except:
            # 路径
            if path.endswith("/") and not self.map["GET"].get(path):
                # 返回默认的html文件
                try:
                    # 存在返回字节 
                    return self.filebyte("{}{}".format(path[1:],self.default))
                except:
                    # 不存在返回 None
                    return None
            else:
                try:
                    # print(f"{method} {path}")
                    # 路径存在，返回函数对应结果（二进制码）,如果是字典先转json
                    return self.jsonify(self.map[method][path]()).encode()
                except:
                    # self.map 找不到映射路径
                    return None

class Response(object):
    def __init__(self):
        self.OK = b'''\
HTTP/1.1 200 OK

'''
        self.NOTFOUND = b'''\
HTTP/1.1 404 Error

{"status":404}
'''
        self.ERROR = b'''\
HTTP/1.1 500 Error

{"status":500}
'''
    def bytedata(self,data):
        return self.OK + data + b'\n' if data else self.NOTFOUND

class APP(object):
    def __init__(self):
        self.req = Request()
        self.route = Route()
        self._route = self.route.route
        self.resp = Response()

    @property
    def router(self):
        return self._route
       
    def run(self):
        listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # (重要)设置端口释放后立即就可以被再次使用
        listen_socket.bind(("0.0.0.0",80))  # 绑定地址        
        listen_socket.listen(5)  # 开启监听（最大连接数5）

        res = self.resp.ERROR
        while True:
            try:
                client_connection, client_address = listen_socket.accept()
                request = client_connection.recv(1024) # BUFSIZE = 1024

                self.req.set(request)
                data = self.route.load(self.req.method,self.req.url)
                res = self.resp.bytedata(data)

            except:
                res = self.resp.ERROR

            finally:
                client_connection.sendall(res)
                client_connection.close()